﻿/*
Copyright (c) 2010 Vratislav Cermak (vratacermak@gmail.com)
 
This file is part of TeachMe Project. See http://code.google.com/p/teach-me for more information.
 
TeachMe is an open source project. Using of TeachMe is free of charge.
It is distributed under GNU General Public License version 3 (GPLv3).
GPLv3 can be found at http://www.gnu.org/licenses/gpl.html.
 
This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE.  See the license for more details.
*/

using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Linq;
using System.Windows.Forms;
using TeachMe.Application.Events;
using System.Drawing.Text;

namespace TeachMe.Application.Controls
{
    public class SymbolsControl : Panel
    {
        public event EventHandler<SymbolEventArgs> Selected;

        private class Sector
        {
            public string Character { get; set; }
            public Rectangle Rectangle { get; set; }
            public int Id { get; set; }
        }

        private const int CharacterWidth = 30;
        private const int CharacterHeight = 30;

        private List<Sector> _sectors;
        private readonly Font _font;
        private readonly Pen _pen;
        private int _activeId = -1;

        public SymbolsControl()
        {
            // "Lucida Sans Unicode" is supposed to be IPA font
            FontFamily family = new InstalledFontCollection().Families.FirstOrDefault(x => String.Compare(x.Name, "Lucida Sans Unicode") == 0) ?? FontFamily.GenericSansSerif;

            _font = ObjectFactoryWrappers.CreateFont(family, 18, FontStyle.Regular);
            _pen = new Pen(Brushes.Black) { DashStyle = DashStyle.Dot };

            SetStyle(ControlStyles.DoubleBuffer | ControlStyles.UserPaint | ControlStyles.AllPaintingInWmPaint, true);
        }

        public void SetSymbols(char[] value)
        {
            RecreateContent(value);
            Invalidate();
        }

        protected override void OnPaintBackground(PaintEventArgs e)
        {
            e.Graphics.FillRectangle(new SolidBrush(BackColor), e.ClipRectangle);

            int columns, rows;
            GetSize(out columns, out rows);

            if (columns == 0 || rows == 0)
            {
                return;
            }

            int x = 0, height = (rows + 1) * CharacterHeight;
            for (int col = 0; col < columns; col++)
            {
                if (col > 0)
                {
                    e.Graphics.DrawLine(_pen, x, 0, x, height);
                }
                x += CharacterWidth;
            }

            int y = 0, width = columns * CharacterWidth;
            for (int row = 0; row < rows; row++)
            {
                if (row > 0)
                {
                    e.Graphics.DrawLine(_pen, 0, y, width, y);
                }
                y += CharacterHeight;
            }
        }

        protected override void OnPaint(PaintEventArgs e)
        {
            if (_sectors != null)
            {
                foreach (var sector in _sectors)
                {
                    if (sector.Id == _activeId)
                    {
                        Rectangle hoverRectangle = new Rectangle(sector.Rectangle.Location, sector.Rectangle.Size);
                        hoverRectangle.Inflate(-3, -3);
                        hoverRectangle.Offset(1, 1);
                        e.Graphics.FillRectangle(Brushes.Aqua, hoverRectangle);
                    }

                    SizeF size = e.Graphics.MeasureString(sector.Character, _font);
                    
                    e.Graphics.TextRenderingHint = TextRenderingHint.AntiAlias;
                    e.Graphics.DrawString(sector.Character, _font, Brushes.Black, sector.Rectangle.Left + ((CharacterWidth - size.Width) / 2), sector.Rectangle.Top + ((CharacterHeight - size.Height) / 2));
                }
            }

            base.OnPaint(e);
        }

        protected override void OnResize(EventArgs eventargs)
        {
            if (_sectors != null && _sectors.Count > 0)
            {
                RecreateContent(_sectors.Select(sector => sector.Character.Single()).ToList());
                Invalidate();
            }

            base.OnResize(eventargs);
        }

        private void RecreateContent(ICollection<char> characters)
        {
            _sectors = new List<Sector>();

            if (characters == null || characters.Count == 0)
            {
                return;    
            }

            int left = 0, top = 0, id = 0;
            foreach (char character in characters)
            {
                _sectors.Add(new Sector { Character = character.ToString(), Rectangle = new Rectangle(left, top, CharacterWidth, CharacterHeight), Id = id++ });
                if (left + CharacterWidth * 2 > Width)
                {
                    left = 0;
                    top = top + CharacterHeight;
                }
                else
                {
                    left += CharacterWidth;
                }
            }

            Height = GetHeightInPixels();
        }

        private void GetSize(out int columns, out int rows)
        {
            if (_sectors == null || _sectors.Count == 0)
            {
                columns = 0;
                rows = 0;
            }
            else 
            {
                columns = Math.Min(Width/CharacterWidth, _sectors.Count);
                if (columns > 0) 
                {
                    rows = _sectors.Count > columns ? _sectors.Count / columns : 1;
                    if (_sectors.Count % columns > 0) 
                    {
                        rows++;
                    }
                }
                else 
                {
                    columns = 0;
                    rows = 0;
                }
            }
        }

        private int GetHeightInPixels()
        {
            int columns, rows;
            GetSize(out columns, out rows);
            return Math.Max(1, rows) * CharacterHeight;
        }

        protected override void OnMouseMove(MouseEventArgs e)
        {
            if (_sectors == null) 
            {
                return;
            }

            _activeId = -1;

            foreach (var sector in _sectors.Where(sector => sector.Rectangle.Contains(e.X, e.Y))) 
            {
                _activeId = sector.Id;
                break;
            }

            Invalidate();
        }

        protected override void OnMouseLeave(EventArgs e)
        {
            _activeId = -1;

            Invalidate();
            
            base.OnMouseLeave(e);
        }

        protected override void OnMouseClick(MouseEventArgs e)
        {
            if (_sectors != null)
            {
                foreach (var sector in _sectors)
                {
                    if (sector.Rectangle.Contains(e.X, e.Y))
                    {
                        EventHandler<SymbolEventArgs> handler = Selected;
                        if (handler != null)
                        {
                            handler(this, new SymbolEventArgs { Symbol = sector.Character.Single() });
                        }
                    }
                }
            }
            
            base.OnMouseClick(e);
        }

        public bool IsValidSelection
        {
            get
            {
                return _sectors.Any(sector => RectangleToScreen(sector.Rectangle).Contains(MousePosition.X, MousePosition.Y));
            }
        }

        public char CurrentSelection
        {
            get
            {
                foreach (var sector in _sectors.Where(sector => RectangleToScreen(sector.Rectangle).Contains(MousePosition.X, MousePosition.Y)))
                {
                    return sector.Character.Single();
                }

                throw new InvalidOperationException();
            }
        }
    }
}
